feat(storage): encrypt local sqlite databases - #2576
Conversation
|
Claude finished @ethicnology's task in 9m 8s —— View job Review: encrypted local SQLite databasesThe overall design is strong — fail-closed key store with write-then-read-back verification, no API that can open the on-disk file without a key, verify-then-swap migration with atomic renames, keying before any other statement, and the key charset is both validated and escaped so the
1. High — a stale background task can create an empty database mid-migration, and recovery then deletes the only surviving copies
Two cheap, complementary fixes:
2. Medium — the encryption key can leak into logs, Sentry, and the init error screen via
|
bullbitcoin_sqlite.sqlitewith SQLite3MultipleCiphers, migrating existing plaintext installs.payjoin.sqliteencrypted from the start. The package is unreleased, so it carries no plaintext population and needs no file migration.Design notes
PayjoinDatabase.openandopenPayjointake a non-nullablerequiredkey, andSqliteDatabaseno longer exposes a constructor that opens the on-disk file without one. Tests exercise the same encrypted path as production.VACUUM INTO,PRAGMA rekey, verification, then atomic rename with backup, and a resumable recovery path on the next launch.Validation
make analyze,make unit-test,make format-check,make bull-ui-check,fvm dart fix --dry-rundevelop: wallet, synchronized testnet transaction, and label remained available after migration and repeated forced restarts; both files stopped starting withSQLite format 3; no migration leftovers.Remaining work before this ships on iOS — @wired-pasteque
Not deferred: the first item is a data-loss regression introduced by this PR and blocks an iOS release.
NSDocumentDirectory, which is included in iCloud/iTunes backups, while the key is stored withfirst_unlock_this_deviceand is not restored onto another device. A restored install therefore holds an encrypted database with no key, and the fail-closed path stops the app from starting. Before this PR the restored database was plaintext and still readable. Proposed fix: exclude both databases from backup (NSURLIsExcludedFromBackupKey, or move them to Application Support). Note that seeds already usefirst_unlock_this_device, so wallets already do not survive a device restore — excluding the databases loses nothing that was previously recoverable.Items 1–3 are implementable and unit-testable without an iOS device. Their final validation is not: it needs real hardware for a backup-and-restore cycle onto a second device, a background task firing before first unlock, and a Keychain reset. An iOS simulator cannot reproduce any of these.
iOS test request
Install the debug build over a plaintext
developinstall holding a non-sensitive testnet wallet only, then confirm:app_flutter/no longer begin withSQLite format 3.